#! /usr/bin/env python
# encoding=utf-8
# -*- python -*-

import sys
import random
import time
sys.path.append("../search/modules/aima")
sys.path.append("../search/algorithms")
sys.path.append("../search/maps")
sys.path.append("../utils")
sys.path.append('../pygraph')

import bfs
from dfs import DFS
from myutils import mostrarGrafo, custo, draw_graph
from search import GraphProblem
from map_australia import australia
from map_romania import romania
from tkgraphview import tkGraphView
from write_dot import Dotter
from Tkinter import Tk, Text, StringVar, Frame, GROOVE, Label, Pack, Grid, Place, Canvas, INSERT, DISABLED, END
from idlelib.WidgetRedirector import WidgetRedirector
import ttk
from random import randrange


def vp_start_gui():
	'''Starting point when module is the main routine.'''
	global val, w, root
	root = Tk()
	root.title('Busca Entre Cidades')
	root.geometry('1200x678+615+297')
	set_Tk_var()
	w = Toplevel (root)
	init()
	root.mainloop()

w = None

def create_Toplevel (root):
	'''Starting point when module is imported by another program.'''
	global w, w_win
	if w:  # So we have only one instance of window.
		return
	w = Toplevel (root)
	w.title('Busca Entre Cidades')
	w.geometry('1200x678+615+297')
	set_Tk_var()
	w_win = Toplevel (w)
	init()

	return w_win

def destroy_Toplevel ():
	global w
	w.destroy()
	w = None


def set_Tk_var():
	# These are Tk variables used passed to Tkinter and must be
	# defined before the widgets using them are created.
	global combobox_mapa, combobox_origem, combobox_destino, combobox_algoritmo, combobox_modelo
	combobox_mapa = StringVar()
	combobox_origem = StringVar()
	combobox_destino = StringVar()
	combobox_algoritmo = StringVar()
	combobox_modelo = StringVar()

def init():
	pass

class Toplevel:
	def __init__(self, master=None):

		# Set background of toplevel window to match
		# current style
		style = ttk.Style()
		theme = style.theme_use()
		default = style.lookup(theme, 'background')
		master.configure(background=default)


		self.Frame1 = Frame (master)
		self.Frame1.place(relx=0.0, rely=0.0, relheight=1.0, relwidth=1.0)
		self.Frame1.configure(relief=GROOVE)
		self.Frame1.configure(borderwidth="2")
		self.Frame1.configure(relief="groove")

		self.Label_Mapa = Label (self.Frame1)
		self.Label_Mapa.place(relx=0.01, rely=0.92, height=19, width=36)
		self.Label_Mapa.configure(text='''Mapa''')

		self.TCombobox_Mapa = ttk.Combobox (self.Frame1, textvariable=combobox_mapa, state='readonly')
		self.TCombobox_Mapa.place(relx=0.01, rely=0.95, height=19
				, width=80)
		self.TCombobox_Mapa.configure(takefocus="")
		value_list = [u'Romênia', u'Austrália']
		self.TCombobox_Mapa.configure(values=value_list)
		self.TCombobox_Mapa.bind('<<ComboboxSelected>>', self._update_values_mapa)
		self.TCombobox_Mapa.current(0)


		self.TCombobox_Origem = ttk.Combobox (self.Frame1, textvariable=combobox_origem, state='readonly')
		self.TCombobox_Origem.place(relx=0.11, rely=0.95, height=19
				, width=140)
		self.TCombobox_Origem.configure(takefocus="", values=sorted(i.title().replace('_', ' ') for i in romania.locations.keys()) + ['Aleatório'])
		self.TCombobox_Origem.current(0)


		self.Label_Origem = Label (self.Frame1)
		self.Label_Origem.place(relx=0.11, rely=0.92, height=19, width=46)
		self.Label_Origem.configure(text='''Origem''')

		self.TCombobox_Destino = ttk.Combobox (self.Frame1, textvariable=combobox_destino, state='readonly')
		self.TCombobox_Destino.place(relx=0.264, rely=0.95, height=19, width=140)
		self.TCombobox_Destino.configure(takefocus="", values=sorted(i.title().replace('_', ' ') for i in romania.locations.keys()) + ['Aleatório'])
		self.TCombobox_Destino.current(0)

		self.Label_Destino = Label (self.Frame1)
		self.Label_Destino.place(relx=0.264, rely=0.92, height=19, width=46)
		self.Label_Destino.configure(text='''Destino''')

		self.TCombobox_Algoritmo = ttk.Combobox (self.Frame1, textvariable=combobox_algoritmo, state='readonly')
		self.TCombobox_Algoritmo.place(relx=0.418, rely=0.95, height=19
				, width=280)
		vals = ['Busca em Largura', 'Busca em Profundidade']
		self.TCombobox_Algoritmo.configure(takefocus="", values=vals)
		self.TCombobox_Algoritmo.current(0)

		self.Label_Modelo = Label (self.Frame1)
		self.Label_Modelo.place(relx=0.6985, rely=0.92, height=19, width=60)
		self.Label_Modelo.configure(text='''Modelo''')

		self.TCombobox_Modelo = ttk.Combobox (self.Frame1, textvariable=combobox_modelo, state='readonly')
		self.TCombobox_Modelo.place(relx=0.6985, rely=0.95, height=19
				, width=70)
		vals = [u'Árvore', 'Grafo']
		self.TCombobox_Modelo.configure(takefocus="", values=vals)
		self.TCombobox_Modelo.current(0)

		self.Label_Algoritmo1 = Label (self.Frame1)
		self.Label_Algoritmo1.place(relx=0.418, rely=0.92, height=19, width=60)
		self.Label_Algoritmo1.configure(text='''Algoritmo''')

		self.TButtonExecutar1 = ttk.Button (self.Frame1)
		self.TButtonExecutar1.place(relx=0.88, rely=0.95, height=25, width=76)
		self.TButtonExecutar1.configure(command=self.buscaCidade)
		self.TButtonExecutar1.configure(takefocus="")
		self.TButtonExecutar1.configure(text='''Executar''')

		self.Scrolledtext1 = ScrolledText (self.Frame1)
		self.Scrolledtext1.place(relx=0.60, rely=0.0, relheight=0.92, relwidth=0.40)

		global canvas
		canvas = Canvas (self.Frame1, bg='blue', state=DISABLED)
		canvas.place(relx=0.0, rely=0.0, relheight=0.92
				, relwidth=0.60)
		canvas.configure(borderwidth="2", width=600, height=600)

	def write(self, txt):
		self.Scrolledtext1.insert(INSERT, txt)


	def executar(self):
		self.Scrolledtext1.delete(1.0, END)
		sys.stdout = self
		# sys.stderr = self
		self.buscaCidade()
		# test_script.HelloWorld()
		sys.stdout = sys.__stdout__
		# sys.stderr = sys.__stderr__

	def buscaCidade(self):
		# self.Scrolledtext1.clear()
		'''Funcão executada ao pressionar o "botão executar" pega os valores para o mapa
		, a cidade de partida e o objetivo em cima do modelo árvore ou grafo, valores ob-
		tidos pelos conteúdos dos combos'''
		self.Scrolledtext1.delete(1.0, END)
		if combobox_mapa.get() == u'Romênia':
			mapa = romania
		else:
			mapa = australia

		if combobox_algoritmo.get() == 'Busca em Largura':
			if combobox_modelo.get() == u'Árvore':
				model = 'tree'
				algorithm = bfs.BFS
			elif combobox_modelo.get() == 'Grafo':
				model = 'graph'
				algorithm = bfs.BFS
			else:
				pass

		if combobox_algoritmo.get() == 'Busca em Profundidade':
			if combobox_modelo.get() == u'Árvore':
				model = 'tree'
				algorithm = DFS
			elif combobox_modelo.get() == 'Grafo':
				model = 'graph'
				algorithm = DFS
			else:
				pass

		# Procura aleatória
		if combobox_origem.get() == 'Aleatório':
			if combobox_mapa.get() == 'Romênia':
				self.TCombobox_Origem.current(randrange(len(romania.locations.keys())))
				time.sleep(0.1)
			else:
				self.TCombobox_Origem.current(randrange(len(australia.dict.keys())))
				time.sleep(0.1)

		if combobox_destino.get() == 'Aleatório':
			if combobox_mapa.get() == 'Romênia':
				self.TCombobox_Destino.current(randrange(len(romania.locations.keys())))
				time.sleep(0.1)
			else:
				self.TCombobox_Destino.current(randrange(len(australia.dict.keys())))
				time.sleep(0.1)


		problem = GraphProblem(combobox_origem.get().upper().replace(' ', '_'), combobox_destino.get().upper().replace(' ', '_'), mapa)
		alg = algorithm(problem, model, self.Scrolledtext1)

		if algorithm == DFS and model == 'tree':
			alg = algorithm(problem, model, limit=1000)
		self.Scrolledtext1.insert(INSERT, u'De %s à %s (%s em %s):\n' % (combobox_origem.get().title().replace('_', ' '), combobox_destino.get().title().replace('_', ' '), combobox_algoritmo.get(), combobox_modelo.get()))
		# o resultado (retorno do algoritmo) é uma lista do tipo:
		# [Nó,[l ista de nós expandidos]]
		resultado = alg.execute()
		self.Scrolledtext1.tag_delete('here')
		[solution, tree, extendidos] = [resultado[0].solution(), resultado[1], resultado[2]]
		self.Scrolledtext1.insert(END, mostrarGrafo(tree, problem))
		self.Scrolledtext1.insert(END, u"Solução: " + problem.initial.title().replace('_', ' ') + '-> ' + '-> '.join([i.title().replace('_', ' ') for i in solution]) + '\n')
		self.Scrolledtext1.insert(END, u"Número de nós extendidos: " + str(extendidos) + '\n')
		self.Scrolledtext1.insert(END, u"Custo Total do Caminho: " + str(custo(problem, solution, mapa)) + "\n ===FIM===\n\n")
		# node_edge_old_style é o formato de dicionário para a exibição gráfica pelo pygraph
		node_edge_old_style = draw_graph(tree, mapa, solution, problem)
		tkGraphView(node_edge_old_style, "Graph Viewer Title", canv=canvas, root=root, graphtype='directed', startTk=False)
		self.Scrolledtext1.see(END)
		# uma segunda opção de saída gráfica usando o formato .dot que pode ser visualizado com o graphviz
		# podemos gerar outros formatos (png, jpg, etc), mas esse mantém a informação sem perdas

		if combobox_mapa.get() == 'Romênia':
			dotter = Dotter(tree, solution, problem.initial,romania)
		else:
			dotter = Dotter(tree, solution, problem.initial,australia)
		dotter.write_dot_file()

	def _update_values_mapa(self, evt):
		# add entered text to combobox list of values
		widget = evt.widget  # get widget
		val = widget.get()  # get values

		if val == u'Romênia':
			vals = sorted(i.title().replace('_', ' ') for i in romania.locations.keys()) + ['Aleatório']
			self.TCombobox_Origem.configure(values=vals,)
			self.TCombobox_Destino.configure(values=vals,)
		else:
			vals = sorted(i.title().replace('_', ' ') for i in australia.dict.keys()) + ['Aleatório']
			self.TCombobox_Origem.configure(values=vals,)
			self.TCombobox_Destino.configure(values=vals,)
		self.TCombobox_Origem.current(0)
		self.TCombobox_Destino.current(0)

		return 'break'  # don't propagate event

	def get_ScrolledText(self):
		return self.Scrolledtext1

# The following code is added to facilitate the Scrolled widgets you specified.
class AutoScroll(object):
	'''Configure the scrollbars for a widget.'''

	def __init__(self, master):
		vsb = ttk.Scrollbar(master, orient='vertical', command=self.yview)
		hsb = ttk.Scrollbar(master, orient='horizontal', command=self.xview)

		self.configure(yscrollcommand=self._autoscroll(vsb),
			xscrollcommand=self._autoscroll(hsb))
		self.grid(column=0, row=0, sticky='nsew')
		vsb.grid(column=1, row=0, sticky='ns')
		hsb.grid(column=0, row=1, sticky='ew')

		master.grid_columnconfigure(0, weight=1)
		master.grid_rowconfigure(0, weight=1)

		methods = Pack.__dict__.keys() + Grid.__dict__.keys() \
				  + Place.__dict__.keys()

		for meth in methods:
			if meth[0] != '_' and meth not in ('config', 'configure'):
				setattr(self, meth, getattr(master, meth))

	@staticmethod
	def _autoscroll(sbar):
		'''Hide and show scrollbar as needed.'''
		def wrapped(first, last):
			first, last = float(first), float(last)
			if first <= 0 and last >= 1:
				sbar.grid_remove()
			else:
				sbar.grid()
			sbar.set(first, last)
		return wrapped

	def __str__(self):
		return str(self.master)

def _create_container(func):
	'''Creates a ttk Frame with a given master, and use this new frame to
	place the scrollbars and the widget.'''
	def wrapped(cls, master, **kw):
		container = ttk.Frame(master)
		return func(cls, container, **kw)
	return wrapped

class ScrolledText(AutoScroll, Text):
	'''A standard Tkinter Text widget with scrollbars that will
	automatically show/hide as needed.'''
	@_create_container
	def __init__(self, master, **kw):
		self.text = Text.__init__(self, master)
		self.redirector = WidgetRedirector(self)
		self.insert = self.redirector.register("insert", lambda *args, **kw: "break")
		self.delete = self.redirector.register("delete", lambda *args, **kw: "break")
		AutoScroll.__init__(self, master)

if __name__ == '__main__':
	vp_start_gui()
